﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.Linq;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace Ogdi.InteractiveSdk.WebForms.Forms
{
    [Serializable]
    class ContinuationToken
    {
        public string NextPartitionKey { get; set; }
        public string NextRowKey { get; set; }
    }

    enum ButtonClicked
    {
        None,
        Run,
        Previous,
        Next
    }



    public partial class DataBrowserForm : System.Web.UI.Page
    {
        int _pageSize = 10;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                try
                {
                    string entitySet = Request.Params["EntitySet"];
                    var container = Request.QueryString["Container"];
                    ViewState["Container"] = container;
                    this.entitySetLabel.Text = entitySet;
                    this.uriLabel.Text = RoleEnvironment.GetConfigurationSettingValue("serviceUri") + container + "/" + entitySet + "/";
                    this.queryBox.Text = "";

                    var result = ServiceInterface.GetData(container, this.entitySetLabel.Text, this.queryBox.Text,
                                                      _pageSize,
                                                      null,
                                                      null);
                    // Page Load is like clicking the Run button automatically 
                    // so we just set the value to ButtonClicked.Run
                    SetPagingAndBind(result, null, ButtonClicked.Run);
                }
                catch
                {
                    //  If an error occurs in parsing the Id query parameter,
                    //      go back to the previous page
                    Response.Redirect("javascript:history.go(-1)");
                }
            }
        }

        protected void runButton_Click(object sender, EventArgs e)
        {
            try
            {
                var result = ServiceInterface.GetData(ViewState["Container"].ToString(),
                                                      this.entitySetLabel.Text,
                                                      this.queryBox.Text,
                                                      _pageSize,
                                                      null,
                                                      null);
                // Page Load is like clicking the Run button automatically 
                // so we just set the value to ButtonClicked.Run
                SetPagingAndBind(result, null, ButtonClicked.Run);
            }
            catch (WebException ex)
            {
                ShowServiceError(ex);
            }
            catch (Exception)
            {
                ShowUnexpectedError();
            }
        }

        protected void languageBox_SelectedIndexChange(object sender, EventArgs e)
        {
            UpdateDataSampleCode();
        }

        protected void maplanguageBox_SelectedIndexChange(object sender, EventArgs e)
        {
            UpdateMapSampleCode();
        }

        protected void PreviousLink_Click(object sender, EventArgs e)
        {
            try
            {
                ContinuationToken nextContinuationToken = null;
                ContinuationToken previousContinuationToken = null;

                var continuationTokenStack = GetContinuationTokenStack();

                // The current token on the stack is the "next" token for 
                // the previous instance of the page.  Since this is a 
                // "previous" click, we don't care about this token so we
                // remove it ("throw it away")
                continuationTokenStack.Pop();
                // The current token on the stack is for the "next" set of 
                // for this instance of the page.  In order to execute the 
                // "previous" query, we need to temporarily pop the "next" 
                // token off the stack.
                previousContinuationToken = continuationTokenStack.Pop();
                // This is the token we need to execute the "previous" query.
                nextContinuationToken = NullPeek(continuationTokenStack);
                // We still need the "next" token to execute the "next" query
                // when the user clicks the NextLink.  Therefore we put it 
                // back on the stack.
                continuationTokenStack.Push(previousContinuationToken);

                XmlPlusContinuation result;

                if (nextContinuationToken != null)
                {
                    result = ServiceInterface.GetData(ViewState["Container"].ToString(), this.entitySetLabel.Text, this.queryBox.Text,
                                                          _pageSize,
                                                          nextContinuationToken.NextPartitionKey,
                                                          nextContinuationToken.NextRowKey);
                }
                else
                {
                    result = ServiceInterface.GetData(ViewState["Container"].ToString(), this.entitySetLabel.Text, this.queryBox.Text,
                                                          _pageSize,
                                                          null,
                                                          null);
                }

                SetPagingAndBind(result, continuationTokenStack, ButtonClicked.Previous);
            }
            catch (WebException ex)
            {
                ShowServiceError(ex);
            }
            catch (Exception)
            {
                ShowUnexpectedError();
            }
        }

        protected void NextLink_Click(object sender, EventArgs e)
        {
            try
            {
                var continuationTokenStack = GetContinuationTokenStack();

                var nextContinuationToken = NullPeek(continuationTokenStack);

                XmlPlusContinuation result;

                if (nextContinuationToken != null)
                {
                    result = ServiceInterface.GetData(ViewState["Container"].ToString(), this.entitySetLabel.Text, this.queryBox.Text,
                                                      _pageSize,
                                                      nextContinuationToken.NextPartitionKey,
                                                      nextContinuationToken.NextRowKey);
                }
                else
                {
                    result = ServiceInterface.GetData(ViewState["Container"].ToString(), this.entitySetLabel.Text, this.queryBox.Text,
                                                      _pageSize,
                                                      null,
                                                      null);
                }

                SetPagingAndBind(result, continuationTokenStack, ButtonClicked.Next);
            }
            catch (WebException ex)
            {
                ShowServiceError(ex);
            }
            catch (Exception)
            {
                ShowUnexpectedError();
            }
        }

        private void SetPagingAndBind(XmlPlusContinuation xmlPlusContinuation,
                           Stack<ContinuationToken> continuationTokenStack,
                           ButtonClicked buttonClicked)
        {
            if (xmlPlusContinuation.Xml.Elements().Count() != 0)
            {
                string nextPartitionKey = xmlPlusContinuation.NextPartitionKey;
                string nextRowKey = xmlPlusContinuation.NextRowKey;

                switch (buttonClicked)
                {
                    case ButtonClicked.Run:
                        PreviousLink.Enabled = false;

                        continuationTokenStack = new Stack<ContinuationToken>();

                        if (nextPartitionKey != null && nextRowKey != null)
                        {
                            var nextContinuationToken = new ContinuationToken
                            {
                                NextPartitionKey = nextPartitionKey,
                                NextRowKey = nextRowKey
                            };

                            continuationTokenStack.Push(nextContinuationToken);

                            NextLink.Enabled = true;
                        }

                        if (continuationTokenStack.Count == 0)
                        {
                            NextLink.Enabled = false;
                        }
                        break;
                    //case ButtonClicked.Run:
                    //    if (continuationTokenStack.Count > 1)
                    //    {
                    //        PreviousLink.Enabled = true;
                    //    }
                    //    else
                    //    {
                    //        PreviousLink.Enabled = false;
                    //    }
                    //    break;
                    case ButtonClicked.Previous:
                        if (continuationTokenStack.Count == 1)
                        {
                            PreviousLink.Enabled = false;
                        }
                        NextLink.Enabled = true;
                        break;
                    case ButtonClicked.Next:

                        if (nextPartitionKey != null && nextRowKey != null)
                        {
                            var nextContinuationToken = new ContinuationToken
                            {
                                NextPartitionKey = nextPartitionKey,
                                NextRowKey = nextRowKey
                            };

                            continuationTokenStack.Push(nextContinuationToken);
                            NextLink.Enabled = true;
                            PreviousLink.Enabled = true;
                        }
                        else
                        {
                            // The else condition means there are no more pages of data.
                            // The PreviousLink click handler ALWAYS throws away what
                            // is on top of the stack.  Therefore, we add a bogus token.
                            continuationTokenStack.Push(new ContinuationToken
                            {
                                NextPartitionKey = "X",
                                NextRowKey = "X"
                            });
                            NextLink.Enabled = false;
                        }
                        break;
                }

                ViewState.Add("ContinuationTokenStack", continuationTokenStack);

                //  Show query results
                ShowResults(xmlPlusContinuation.Xml);

                //  Show sample code
                UpdateDataSampleCode();
                UpdateMapSampleCode();
            }
            else
            {
                ShowError("Your query returned 0 results.", "Please check your query syntax and try again.");
            }
        }

        private Stack<ContinuationToken> GetContinuationTokenStack()
        {
            return ViewState["ContinuationTokenStack"] as Stack<ContinuationToken>;
        }

        private ContinuationToken NullPeek(Stack<ContinuationToken> stack)
        {
            try
            {
                return stack.Peek();
            }
            catch
            {
                return null;
            }
        }

        protected void ShowResults(XElement data)
        {
            var propertiesElements = data.Elements("properties");
            int nResults = propertiesElements.Count();

            //Old Code
            //dataResultsGrid.Columns.Clear();

            //Section 508 - Showing table
            tblEntitySet.Rows.Clear();

            if (nResults > 0)
            {
                // We need to dynamically create column templates
                // Since we CANNOT set dataResultsGrid.AutoGenerateColumns=true
                // Base the columns off of the metadata in the EntityMetadata table
                var tableName = data.Attribute("tableName").Value;

                // TODO:    Once the UI has been updated to support multiple 
                //          "containers" (table storage accounts), then update
                //          this code to use ASP.NET Cache to cache the entity
                //          metadata based on the container/entityset as the key.
                //          This will prevent us from constantly hitting Azure Tables
                //          for metadata that will almost never change.  

                //          Cache duration TBD (daily seems reasonable)
                var filter = "entityset eq '" + tableName + "'";
                var enityMetaData = ServiceInterface.GetData(ViewState["Container"].ToString(), "EntityMetadata", filter, 1000, null, null);
                var properties = enityMetaData.Xml.Elements("properties");
                properties.Elements("entityset").Remove();
                properties.Elements("entitykind").Remove();
                var propertyMetaData = enityMetaData.Xml.Elements("properties").First().Elements();
                // ENDTODO

                //Old Code
                //foreach (var property in propertyMetaData)
                //{
                //    var tf = new TemplateField();
                //    if (property.Name.LocalName != "entityid")
                //    {
                //        tf.HeaderTemplate = new CustomGridViewTemplate(DataControlRowType.Header, property.Name.LocalName);
                //        tf.ItemTemplate = new CustomGridViewTemplate(DataControlRowType.DataRow, property.Name.LocalName);
                //    }
                //    else
                //    {
                //        tf.HeaderTemplate = new EntityIdCustomGridViewTemplate(DataControlRowType.Header, property.Name.LocalName);
                //        tf.ItemTemplate = new EntityIdCustomGridViewTemplate(DataControlRowType.DataRow, property.Name.LocalName);
                //    }
                //    //Old Code
                //    //dataResultsGrid.Columns.Add(tf);
                //}

                ArrayList dataClmnHeaders = new ArrayList();
                foreach (var property in propertyMetaData)
                {
                    dataClmnHeaders.Add(property.Name);
                }

                HideError();
                //Old Code
                //dataResultsGrid.Visible = true;

                //Section 508 - Showing table
                tblEntitySet.Visible = true;

                //Old Code
                //exportPanel.Visible = true;
                //XmlDataSource1.Data = data.ToString();
                
                //Old Code
                //dataResultsGrid.DataSource = XmlDataSource1;
                //dataResultsGrid.DataBind();

                //Section 508 - Code to show the Entity set data in table format
                XmlDocument inputDoc = new XmlDocument();
                inputDoc.LoadXml(data.ToString());

                XmlNodeList propNodeList = inputDoc.GetElementsByTagName("properties");
                if (propNodeList != null)
                {
                    TableHeaderRow tHdr = new TableHeaderRow();
                    tHdr.BackColor = System.Drawing.Color.FromArgb(95, 154, 199);
                    for (int j = 0; j < dataClmnHeaders.Count; j++)
                    {
                        TableHeaderCell tHcell = new TableHeaderCell();
                        tHcell.ForeColor = System.Drawing.Color.White;
                        tHcell.Font.Bold = true;
                        tHcell.Attributes.Add("scope", "col");
                        tHcell.Attributes.Add("id", dataClmnHeaders[j].ToString());
                        tHcell.Wrap = false;
                        tHcell.Text = "<span>" + dataClmnHeaders[j].ToString() + "</span>";
                        tHdr.Cells.Add(tHcell);
                    }
                    tblEntitySet.Rows.Add(tHdr);

                    for (int i = 0; i < propNodeList.Count; i++)
                    {
                        if (propNodeList[i].ChildNodes.Count > 0)
                        {
                            TableRow tr = new TableRow();

                            if (i % 2 == 0)
                            {
                                tr.Attributes.Add("style", "border:1 solid #F0F0F0 ; background-color:#FEFEFE");
                            }
                            else
                            {
                                tr.Attributes.Add("style", "border:1 solid #F0F0F0 ; background-color:#F4F4F4");
                            }

                            TableHeaderCell tblHeaderCell = new TableHeaderCell();

                            for (int j = 0; j < dataClmnHeaders.Count; j++)
                            {
                                XmlNode indNode = propNodeList[i].SelectSingleNode(dataClmnHeaders[j].ToString());

                                if (indNode != null)
                                {
                                    if (indNode.Name == dataClmnHeaders[0].ToString())
                                    {
                                        tblHeaderCell.Attributes.Add("id", indNode.Name + i);
                                        tblHeaderCell.Wrap = false;
                                        tblHeaderCell.Style.Add("text-align", "left");
                                        tblHeaderCell.Text = "<img title=\"" + indNode.Name + "\" src=\"images/key.png\" alt=\"Entity Id\" longdesc=\"desc/KeyImg.txt\" style=\"height:15px;border-width:0px;\"/>";
                                        tr.Cells.Add(tblHeaderCell);
                                    }
                                    else
                                    {
                                        TableCell tblCell = new TableCell();
                                        tblCell.Attributes.Add("headers", indNode.Name);
                                        tblCell.Wrap = false;
                                        tblCell.Style.Add("text-align", "left");
                                        tblCell.Text = "<span>" + indNode.InnerText + "</span>";
                                        tr.Cells.Add(tblCell);
                                    }
                                }
                                else
                                {
                                    if (j == 0)
                                    {
                                        tblHeaderCell.Attributes.Add("id", dataClmnHeaders[j].ToString() + i);
                                        tblHeaderCell.Wrap = false;
                                        tblHeaderCell.Style.Add("text-align", "left");
                                        tblHeaderCell.Text = "<img title=\"" + dataClmnHeaders[j].ToString() + "\" src=\"images/key.png\" alt=\"Entity Id\" longdesc=\"desc/KeyImg.txt\" style=\"height:15px;border-width:0px;\"/>";
                                        tr.Cells.Add(tblHeaderCell);
                                    }
                                    else
                                    {
                                        TableCell tblCell = new TableCell();
                                        tblCell.Attributes.Add("headers", dataClmnHeaders[j].ToString());
                                        tblCell.Wrap = false;
                                        tblCell.Style.Add("text-align", "left");
                                        tblCell.Text = "<span> </span>";
                                        tr.Cells.Add(tblCell);
                                    }
                                }
                            }
                            tblEntitySet.Rows.Add(tr);
                        }
                    }
                }
                //End

                var hyperlink = this.uriLabel.Text;
                if (this.queryBox.Text.Trim() != string.Empty)
                {
                    hyperlink += "?$filter=" + this.queryBox.Text;
                }
                dataFullQueryUrlHyperlink.Text = hyperlink;
                dataFullQueryUrlHyperlink.NavigateUrl = hyperlink;
                dataFullQueryUrlPanel.Visible = true;
            }
            else
            {
                dataFullQueryUrlPanel.Visible = false;
                //Old Code
                //dataResultsGrid.Visible = false;

                //Section 508 - Showing table
                tblEntitySet.Visible = true;

                nResults = 0;
            }

            // Now that we paging (using Azure Table continuation approach), we do not have access
            // to the total results returned. 

            //dataResultsLabel.Text = String.Format("Query suceeded.  {0} results returned.", nResults);
        }


        protected void UpdateDataSampleCode()
        {
            SampleCodeGenerator codeGen = new SampleCodeGenerator(languageBox.SelectedValue);

            var queryString = this.queryBox.Text.Replace("\"", "\\\"");
            string baseUri = RoleEnvironment.GetConfigurationSettingValue("serviceUri") + ViewState["Container"];
            string code = codeGen.GenerateCSharp(baseUri, this.entitySetLabel.Text, queryString);

            dataSampleCodeLiteral.InnerHtml = code;
        }

        protected void UpdateMapSampleCode()
        {
            SampleCodeGenerator codeGen = new SampleCodeGenerator(mapLanguageBox.SelectedValue);

            var queryString = this.queryBox.Text.Replace("\"", "\\\"");
            string code = codeGen.GenerateHtml(this.uriLabel.Text, queryString);

            mapSampleCodeLiteral.InnerHtml = code;
        }

        protected void ShowError(string message, string detail)
        {
            errorLabel.Text = message;
            errorDetailsLabel.Text = detail;
            errorPanel.Visible = true;
        }

        protected void ShowError(string detail)
        {
            ShowError("Your request could not be completed. Please try again.", detail);
        }

        private void ShowServiceError(WebException ex)
        {
            ShowError("\"" + ex.Message +
                      "\" This indicates the request to the OGDI service failed.  Check your query for syntax errors and try again.");
        }

        private void ShowUnexpectedError()
        {
            ShowError("Unexpected error.");
        }

        protected void HideError()
        {
            errorPanel.Visible = false;
        }

        //DAISY Plugin - Link Button Click event for DTBook translation(DAISY format XML)
        protected void DAISYLink_Click(object sender, EventArgs e)
        {
            try
            {
                //Uncomment the following code block if developer want to do Load Testing
                //Code to note down the time before calling the method to get data from Azure
                //LogEntry logEntry = new LogEntry();
                // logEntry.Message = "Starting : " + "Before getting data from azure : " + DateTime.Now.ToString("yyyy-MM-dd-HH:m:s:FFF");
                //Logger.Write(logEntry, "OGDI Time Calculation");

                //method that gets the data in the XML format from Azure Environment
                XmlPlusContinuation xmlPlusContinuation = ServiceInterface.GetDaisyData(ViewState["Container"].ToString(),
                                                          this.entitySetLabel.Text,
                                                          this.queryBox.Text, null, null);

                //Uncomment the following code block if developer want to do Load Testing
                //Code to note down the time after getting getting data from Azure
                //logEntry.Message = "After getting data from azure : " + DateTime.Now.ToString("yyyy-MM-dd-HH:m:s:FFF");
                // Logger.Write(logEntry, "OGDI Time Calculation");

                //Function to get the header information of a particular entity set
                XElement dataInput = MetadataInput(xmlPlusContinuation.Xml);
                String xmlDataFromAzure = xmlPlusContinuation.Xml.ToString();

                #region Load Testing Code Block

                //XmlDocument docInput = new XmlDocument();
                //docInput.LoadXml(xmlDataFromAzure);

                //XmlNode firstPropNode = docInput.DocumentElement.FirstChild;

                ////Removing the first child of Root node
                //if (docInput.DocumentElement.ChildNodes.Count > 0)
                //{
                //    docInput.DocumentElement.RemoveChild(docInput.DocumentElement.FirstChild);
                //}

                //XmlDocument docTemp = docInput;

                //for (int i = 0; i < 5; i++)
                //{
                //    docTemp.DocumentElement.InnerXml = docTemp.DocumentElement.InnerXml.Insert(docTemp.DocumentElement.InnerXml.Length, docInput.DocumentElement.InnerXml);
                //}

                //docTemp.DocumentElement.InsertBefore(firstPropNode, docTemp.DocumentElement.FirstChild);

                ////Code to note down the number of records in the input XML
                //logEntry.Message = "Number of Records : " + docTemp.DocumentElement.ChildNodes.Count;
                //Logger.Write(logEntry, "OGDI Time Calculation");

                ////Code to note down the time before starting the tarnslation and validation 
                //logEntry.Message = "Before translation : " + DateTime.Now.ToString("yyyy-MM-dd-HH:m:s:FFF");
                //Logger.Write(logEntry, "OGDI Time Calculation");

                #endregion

                //If developer wants to do Load testing, uncomment the above region and Use "docTEmp.InnerXml"
                //as the parameter for the below String builder class.
                //Converting data in string format to XMLReader format
                TextReader inputXMLDataForTrans = new StringReader(xmlDataFromAzure);
                XmlReader xmlReader = XmlReader.Create(inputXMLDataForTrans);

                /*Calling the method that does actual translation of xml data
                coming from Azure to DTBook xml*/
                DTBookTranslation.DTBook objBook = new DTBookTranslation.DTBook();
                StringBuilder sbforXML = new StringBuilder();
                TextReader validatedDTBookXml = new StringReader(sbforXML.ToString());

                //Xml got from azure environment is given as input for DTBook translation method
                validatedDTBookXml = objBook.TranslationOfAzureXml(xmlReader, Server.MapPath(RoleEnvironment.GetConfigurationSettingValue("pathDTD")));

                //Uncomment the following code block if developer want to do Load Testing
                //Code to note down the time after tarnslation and validation 
                //logEntry.Message = "After translation : " + DateTime.Now.ToString("yyyy-MM-dd-HH:m:s:FFF");
                //Logger.Write(logEntry, "OGDI Time Calculation");

                XmlDocument doc = new XmlDocument();
                doc.Load(validatedDTBookXml);

                //Save Dialog Box for the users to save the translated DTBook Xml file in their local directories
                Response.Clear();
                Response.ContentType = "text/plain";
                string docName = doc.GetElementsByTagName("doctitle").Item(0).InnerText;
                Response.AppendHeader("Content-Disposition", "attachment; filename=" + docName + ".xml");
                doc.Save(Response.OutputStream);
                Response.End();
            }
            catch (WebException ex)
            {
                ShowServiceError(ex);
            }
            catch
            {
                ShowUnexpectedError();
            }
        }

        //Function to get all the Headers for a particular Entity Set
        private XElement MetadataInput(XElement data)
        {
            var propertiesElements = data.Elements("properties");
            int nResults = propertiesElements.Count();

            if (nResults > 0)
            {
                // We need to dynamically create column templates
                // Since we CANNOT set dataResultsGrid.AutoGenerateColumns=true
                // Base the columns off of the metadata in the EntityMetadata table
                var tableName = data.Attribute("tableName").Value;

                // TODO:    Once the UI has been updated to support multiple 
                //          "containers" (table storage accounts), then update
                //          this code to use ASP.NET Cache to cache the entity
                //          metadata based on the container/entityset as the key.
                //          This will prevent us from constantly hitting Azure Tables
                //          for metadata that will almost never change.  

                //Cache duration TBD (daily seems reasonable)
                var filter = "entityset eq '" + tableName + "'";
                var enityMetaData = ServiceInterface.GetData(ViewState["Container"].ToString(), "EntityMetadata", filter, 1000, null, null);
                data.FirstNode.AddBeforeSelf(enityMetaData.Xml.FirstNode);
                return data;
            }
            else
                return data;
        }


    }
}
